﻿using System;
using System.Collections.Generic;
using System.Xml.Serialization;

namespace Bambit.XmlRpc
{
	/// <summary>
	/// A response to a <see cref="MethodCall"/>
	/// </summary>
	/// <remarks>When accessing a <see cref="MethodResponse"/> object, it is prefered to 
	/// utilize either the <see cref="Fault"/> property or the <see cref="Parameters"/>
	/// property instead of the <see cref="Item"/> propery.  These will provide direct
	/// access to the internal data.
	/// Note that when accessing either the <see cref="Fault"/> or <see cref="Parameters"/>
	/// properties, the value will be NULL if the <see cref="ResponseType"/> does not match</remarks>
	[Serializable]
	[XmlType(AnonymousType = true)]
	[XmlRoot("methodResponse", IsNullable = false)]
	public class MethodResponse
	{

		#region Class Data
		private ParameterCollection _Parameters;

		private Fault _Fault;
		#endregion

		#region Ctors

		/// <summary>
		/// Initializes a new instance of the <see cref="MethodResponse"/> class.
		/// </summary>
		public MethodResponse()
		{
			_Parameters=new ParameterCollection();
			_Fault=new Fault();
		}

		#endregion

		#region Properties

		/// <summary>
		/// Gets or sets the type of the response this instance is
		/// </summary>
		/// <value>The type of the response this instance os.</value>
		[XmlIgnore]
		public MethodResponseType ResponseType { get; private set; }

		/// <summary>
		/// Gets or sets the parameters.
		/// </summary>
		/// <value>The parameters of this instance if it is a <see cref="MethodResponseType.Normal"/> response; otherwise <c>null</c>.</value>
		/// <remarks>If the <see cref="Parameters"/> is set, the <see cref="ResponseType"/> is automaically
		/// set to <see cref="MethodResponseType.Normal"/></remarks>
		[XmlIgnore]
		public List<Parameter> Parameters
		{
			get
			{
				return ResponseType==MethodResponseType.Fault ? null : _Parameters.Parameters;
			}
			set
			{
				_Parameters=new ParameterCollection(value);
				ResponseType = MethodResponseType.Normal;
			}
		}

		/// <summary>
		/// Gets or sets the fault.
		/// </summary>
		/// <value>The fault of this instance if it is a <see cref="MethodResponseType.Fault"/>; otherwise null.</value>
		/// <remarks>If the <see cref="Fault"/> is set, the <see cref="ResponseType"/> is automatically
		/// set as <see cref="MethodResponseType.Fault"/></remarks>
		public Fault Fault
		{
			get
			{
				return ResponseType==MethodResponseType.Normal ? null : _Fault;
			}
			set
			{
				_Fault = value;
				ResponseType = MethodResponseType.Fault;
			}
		}

		/// <summary>
		/// Gets or sets the item.
		/// </summary>
		/// <value>The item this response wraps.</value>
		/// <exception cref="InvalidCastException">The supplied object being assigned was NOT of either a 
		/// <see cref="Fault"/> or a <see cref="ParameterCollection"/></exception>
		[XmlElement("fault", typeof(Fault))]
		[XmlElement("params", typeof(ParameterCollection))]
		public object Item
		{
			get
			{
				if (MethodResponseType.Fault == ResponseType)
					return _Fault;
				return _Parameters;
			}
			set
			{
				if (typeof(Fault).IsAssignableFrom(value.GetType()))
				{
					ResponseType = MethodResponseType.Fault;
					_Fault = (Fault)value;
				}
				else if (typeof(ParameterCollection).IsAssignableFrom(value.GetType()))
				{
					ResponseType = MethodResponseType.Normal;
					_Parameters = (ParameterCollection)value;
				}
				else
					throw new InvalidCastException("Method Response can contain only faults or parameters");
			}
		}

		#endregion

	}
}
